<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8"/>
    <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
    <title>自动刷新日志查看器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
            background: #f8f9fa;
            color: #333;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        header {
            background: white;
            padding: 16px 24px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
            z-index: 10;
        }

        h1 {
            font-size: 22px;
            font-weight: 600;
            color: #2d3748;
            text-align: center;
        }

        .controls {
            display: flex;
            gap: 12px;
            justify-content: center;
            margin-top: 16px;
            flex-wrap: wrap;
        }

        .btn {
            background: #4299e1;
            color: white;
            border: none;
            padding: 10px 20px;
            font-size: 16px;
            border-radius: 8px;
            cursor: pointer;
            transition: background 0.2s;
        }

        .btn:hover {
            background: #3182ce;
        }

        .btn:disabled {
            background: #cbd5e0;
            cursor: not-allowed;
        }

        .status {
            margin-top: 12px;
            font-size: 14px;
            color: #718096;
            text-align: center;
        }

        .log-container {
            flex: 1;
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 16px;
            overflow-y: auto;
            font-family: 'Consolas', 'Courier New', monospace;
            line-height: 1.6;
            position: relative;
        }

        .log-item {
            padding: 4px 0;
            opacity: 0;
            animation: fadeIn 0.2s forwards;
        }

        @keyframes fadeIn {
            to {
                opacity: 1;
            }
        }

        .log-time {
            color: #6a9955;
            margin-right: 8px;
        }

        .log-tag {
            color: #9cdcfe;
            margin-right: 8px;
        }

        .log-level {
            font-weight: bold;
            padding: 0 6px;
            border-radius: 4px;
            margin: 0 6px;
        }

        .level-INFO {
            color: #4da6ff;
            background: rgba(77, 166, 255, 0.1);
        }

        .level-WARN {
            color: #ffaa33;
            background: rgba(255, 170, 51, 0.1);
        }

        .level-ERROR {
            color: #ff6666;
            background: rgba(255, 102, 102, 0.15);
        }

        .log-unmatched {
            color: #888;
            font-style: italic;
        }

        .back-to-bottom {
            position: absolute;
            bottom: 20px;
            right: 20px;
            background: #4299e1;
            color: white;
            border: none;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            cursor: pointer;
            display: none;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
            font-size: 18px;
            align-items: center;
            justify-content: center;
            z-index: 100;
        }

        .back-to-bottom.show {
            display: flex;
        }
    </style>
</head>
<body>
<header>
    <h1>自动刷新日志查看器</h1>
    <div class="controls">
        <label class="btn" for="logFile">📁 选择日志文件</label>
        <button class="btn" disabled id="reloadBtn">🔄 立即刷新</button>
        <button class="btn" id="pauseBtn">⏸️ 暂停自动刷新</button>
    </div>
    <input accept=".log,.txt" id="logFile" style="display:none" type="file"/>
    <div class="status" id="status">请上传一个 .log 或 .txt 文件</div>
</header>

<div class="log-container" id="logContainer"></div>
<button class="back-to-bottom" id="backToBottom" title="回到最新日志">↓</button>

<script>
    const logContainer = document.getElementById('logContainer');
    const fileInput = document.getElementById('logFile');
    const reloadBtn = document.getElementById('reloadBtn');
    const pauseBtn = document.getElementById('pauseBtn');
    const statusEl = document.getElementById('status');
    const backToBottomBtn = document.getElementById('backToBottom');

    let currentFile = null;
    let autoScroll = true;
    let scrollTimeout = null;
    let autoRefreshInterval = null;
    let isPaused = false;
    let lastContentHash = '';

    // 安全转义 HTML
    function escapeHtml(str) {
        const div = document.createElement('div');
        div.textContent = str;
        return div.innerHTML;
    }

    // 更宽松的日志正则
    const logRegex = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[([^\]]+)\]\s+(INFO|WARN|ERROR)\s+(.+)$/;

    function renderLogLine(rawLine) {
        const div = document.createElement('div');
        div.className = 'log-item';

        const match = rawLine.match(logRegex);
        if (match) {
            const [, time, tag, level, message] = match;
            const levelClass = `level-${level}`;
            div.innerHTML = `
          <span class="log-time">${time}</span>
          <span class="log-tag">[${tag}]</span>
          <span class="log-level ${levelClass}">${level}</span>
          ${escapeHtml(message)}
        `;
        } else {
            div.className += ' log-unmatched';
            div.textContent = rawLine;
        }
        return div;
    }

    // 简单内容哈希（用于判断是否变化）
    function simpleHash(str) {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            const char = str.charCodeAt(i);
            hash = (hash << 5) - hash + char;
            hash = hash & hash; // 转为32位整数
        }
        return hash.toString();
    }

    function readAndDisplayFile(file, isAuto = false) {
        if (!file) return;

        const reader = new FileReader();
        reader.onload = (e) => {
            const content = e.target.result;
            const newHash = simpleHash(content);

            // 只有内容变化才更新（避免无意义重绘）
            if (newHash === lastContentHash) {
                if (!isAuto) statusEl.textContent = `✅ 文件无变化（${file.name}）`;
                return;
            }

            lastContentHash = newHash;
            const lines = content.split('\n').filter(line => line.trim() !== '');

            logContainer.innerHTML = '';
            lines.forEach(line => {
                logContainer.appendChild(renderLogLine(line));
            });

            // 自动滚动到底部（仅当处于自动滚动模式）
            if (autoScroll) {
                logContainer.scrollTop = logContainer.scrollHeight;
            }

            statusEl.textContent = `✅ ${isAuto ? '自动' : '手动'}加载 ${lines.length} 行（${file.name}）`;
        };
        reader.onerror = () => {
            statusEl.textContent = '❌ 文件读取失败';
        };
        reader.readAsText(file, 'utf-8');
    }

    // 启动自动刷新
    function startAutoRefresh() {
        if (autoRefreshInterval) clearInterval(autoRefreshInterval);
        autoRefreshInterval = setInterval(() => {
            if (currentFile && !isPaused) {
                readAndDisplayFile(currentFile, true);
            }
        }, 1000); // 每1秒
    }

    // 停止自动刷新
    function stopAutoRefresh() {
        if (autoRefreshInterval) {
            clearInterval(autoRefreshInterval);
            autoRefreshInterval = null;
        }
    }

    // 滚动控制
    logContainer.addEventListener('scroll', () => {
        const {scrollTop, scrollHeight, clientHeight} = logContainer;
        const isAtBottom = scrollTop + clientHeight >= scrollHeight - 5;

        if (!isAtBottom) {
            autoScroll = false;
            backToBottomBtn.classList.add('show');
        } else {
            autoScroll = true;
            backToBottomBtn.classList.remove('show');
        }

        clearTimeout(scrollTimeout);
        scrollTimeout = setTimeout(() => {
            const isNowAtBottom = logContainer.scrollTop + logContainer.clientHeight >= logContainer.scrollHeight - 5;
            autoScroll = isNowAtBottom;
            if (autoScroll) backToBottomBtn.classList.remove('show');
        }, 1500);
    });

    backToBottomBtn.addEventListener('click', () => {
        logContainer.scrollTop = logContainer.scrollHeight;
        autoScroll = true;
        backToBottomBtn.classList.remove('show');
    });

    // 文件选择
    fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (file) {
            currentFile = file;
            lastContentHash = ''; // 重置哈希
            reloadBtn.disabled = false;
            isPaused = false;
            pauseBtn.textContent = '⏸️ 暂停自动刷新';

            statusEl.textContent = `正在加载 ${file.name}...`;
            readAndDisplayFile(file, false);
            startAutoRefresh();
        }
    });

    // 手动刷新
    reloadBtn.addEventListener('click', () => {
        if (currentFile) {
            statusEl.textContent = `正在手动刷新 ${currentFile.name}...`;
            readAndDisplayFile(currentFile, false);
        }
    });

    // 暂停/恢复自动刷新
    pauseBtn.addEventListener('click', () => {
        isPaused = !isPaused;
        pauseBtn.textContent = isPaused ? '▶️ 恢复自动刷新' : '⏸️ 暂停自动刷新';
        if (!isPaused && currentFile) {
            readAndDisplayFile(currentFile, true); // 立即刷新一次
        }
    });

    // 页面卸载时清理
    window.addEventListener('beforeunload', () => {
        stopAutoRefresh();
    });
</script>
</body>
</html>